什麼是Flux設計、實作簡單ATM範例了解Redux三大原則feat.vanilla Javascript
2022-11-23 Wed
本文提及以下內容
- 前言
- flu字根
- flux設計模式-單向資料流
- Redux
- 使用vanilla Javascript ATM範例
- 小結
前言
了解Redux的概念可以追溯到先前facebook(現在改名叫做meta)公司的演講,他們提出MVC所遇到的問題以及提出flux的設計概念,本篇文章會重點式講述flux的概念,Redux是flux的概念底下所做出的一種實踐,redudx不單只是只能用在React,他是一種狀態管理的library,因此也會以原生js實作一個簡單的範例來理解redux的原理。
Flu字根
介紹flux之前我們先看flu字根的英文
- flu開頭源自拉丁文fluere是flow(流動的意思)
- flush(激流)、fluent(流暢)、fluxion(流動)
- 與flow組合的字
- inflow(流入)、overflow(溢出)
- fl開頭為flow同源的字
- float(飄動)、flood(洪水)
從英文字母可以了解一切的根源在於流,flux主要也是在控管整個資料流動的設計。
Flux設計模式-單向資料流
傳統MVC所遇到的問題
在傳統MVC的設計模式會導致視圖(view)與模型的關係複雜,有可能一個視圖來自於不同的model,也可能導致無限循環的情形產生。如下圖

為了解決資料流混亂的問題,因此提出了flux的設計概念。
如下圖

Flux的角色
- action 促進資料傳遞給dispatcher的輔助方法(規範改變資料的動作)
- dipatcher 收到action和payload廣傳到被註冊的callback(被註冊的store)
- store 應用程式狀態和邏輯處理(也就是被註冊的callback)的容器
- view 根據資料渲染UI和監聽使用者的事件
flux優點
- view專注在顯示資料(不用撰寫邏輯)
- 資料和邏輯統一存放
- 明確定義每個角色使開發者快速理解App行為
Redux
flux設計模式提出,facebook也開源了flux的專案,但目前官方已經處於維護狀態並且推薦了其他狀態管理的library像是Redux、MobX、Recoil。
如下圖

圖片來源:facebook/flux
另外有興趣的人也可以觀看stack overflow的redux開發者的回答Why use Redux over Facebook Flux?更可以了解redux與flux的差別。
接下來講述redux發展的動機。
動機
- SPA(單頁應用程式)蓬勃發展
- 狀態多樣化(例如伺服器資料、暫存資料、本地端資料)
隨著Javascript單頁應用程式發展,需要管理許多狀態,這些狀態包含伺服器、資料暫存、本地端還沒有正式儲存到伺服器的資料等等,即便react簡化了事件流程,但state還是留給開發者自行管理。
state狀態傳遞的時候遇到的瓶頸

在沒有Redux之前狀態是需要在各個component之間傳遞十分麻煩,有了Redux後將狀態統一儲存在store配發給每個component

UI接收到事件觸發後使用dispatch發送action到store,store經由所接收到的action到reducer處理對應的action後回傳state,UI接收到state 圖片來源Redux Application Data Flow
三大原則
-
- 作為應用程式全域的state儲存了Object tree在單一的store裡面
-
- state只能透過發送action來改變,如此一來確保view或者fetch的callback都不會直接寫入到state,透過集中管理嚴格照順序的逐一觸發。
-
- 撰寫reducer的函式只能是pure function,透過接收先前的state和action回傳新的state
ATM範例
這邊以提款機範例展示Redux
- 一個帳戶內擁有存款
- 可以更改狀態(存款)的ATM提款機
提款機範例最小化redux基本要素包含以下
- action物件
- initState物件
- reducer函式
- store
提款機範例中的角色各司其職
action物件
一個最基礎的action物件,帶有type的屬性,用來作為dispatch的參數,稍後將會被reducer函式接受做出對應的動作。 以此範例而言,action可以存款和提款
{
type: 'DEPOSIT'
}
{
type: 'WITHDRAW'
}initState物件
定義state的初始值的物件,換句話說算是資料的初始值,以此範例為一個key為money,值為1000的物件。
const initState = {
money: 1000
}reducer
function ATMReducer(state = initState, action) {
switch (action.type) {
case 'DEPOSIT':
return { money: state.money + 100 }
case 'WITHDRAW':
return { money: state.money - 100 }
default:
return state
}
}reducer作為改變state的函式,負責邏輯處理,這邊接收兩個參數,第一個參數是state的初始值第二個參數是action,透過switch用來辨認等等接收的dispatch的動作是哪一種類型,以此範例的話解析action的type,將state的物件+100或-100,這邊可以發現撰寫方式是immutable,我們不直接更改state裡面的值,而是創造一個新的物件return回去。
store檔案
//初始化一個store將reducer代入
let store = createStore(ATMReducer);
//當改變發生的時候要做什麼事情
//這裡帶入一個callback
//透過store.getState就可以讀取當前state的東西
store.subscribe(() => console.log(store.getState()));
//發送WITHDRAW的action
store.dispatch({
type: 'WITHDRAW'
});
store.dispatch({
type: 'DEPOSIT'
});
store.dispatch({
type: 'WITHDRAW'
});- createStore()初始化一個store代入reducer
- store.subscribe 改變發生的時候要做什麼事情
- store.dispatch 發送action
完整程式碼如下
import { createStore } from 'redux'
const initState = {
money: 1000,
}
const depositActionCreator = (payload) => (
{
type: 'DEPOSIT',
payload
})
const withdrawActionCreator = (payload) => (
{
type: 'WITHDRAW', payload
}
)
function ATMReducer(state = initState, action) {
switch (action.type) {
case 'DEPOSIT':
return { money: state.money + action.payload }
case 'WITHDRAW':
return { money: state.money - action.payload }
default:
return state
}
}
let store = createStore(ATMReducer);
store.subscribe(() => console.log(store.getState()));
store.dispatch(depositActionCreator(100));
store.dispatch(withdrawActionCreator(200));
store.dispatch(depositActionCreator(500));小結
希望透過vanilla javascript可以更了解redux,如果內容有誤歡迎底下留言,希望以上內容對大家有所幫助。